function getFileMd5(file, complete, error, progress) {
  let blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice,
    chunkSize = 2097152, // Read in chunks of 2MB
    chunks = Math.ceil(file.size / chunkSize),
    currentChunk = 0,
    spark = new SparkMD5.ArrayBuffer(),
    fileReader = new FileReader();

  fileReader.onload = function (e) {
    spark.append(e.target.result); // Append array buffer
    currentChunk++;
    let md5Progress = Math.floor((currentChunk / chunks) * 100);
    if (progress) {
      progress(file, md5Progress);
    }
    if (currentChunk < chunks) {
      loadNext();
    } else {
      if (complete) {
        complete(file, spark.end());
      }
    }
  };
  fileReader.onerror = function () {
    if (error) {
      error(file)
    }
  };

  function loadNext() {
    let start = currentChunk * chunkSize,
      end = ((start + chunkSize) >= file.size) ? file.size : start + chunkSize;
    fileReader.readAsArrayBuffer(blobSlice.call(file, start, end));
  }

  loadNext();
}

function eventCallBack(result, tag) {
  if (result.code == -2) {
    // 上传被取消
  } else if (result.code == -1) {
    // 上传出错
    if (window.failureFunc == undefined) {
      toast.failure(result.msg);
    } else {
      window.failureFunc(result.data)
    }
    progressThis(1, 0);
  } if (result.code == 0) {
    // 上传进度
    progressThis(2, result.data);
  } else if (result.code == 1) {
    // 上传完成
    progressThis(2, 0);
  } else if (result.code == 2) {
    // 上传成功
    // 自动添加MAP到客户端
    addFileMap(result.data, tag, function () {
      if (window.successFunc != undefined) {
        // 返回结果
        window.successFunc(window.host + "/encrypts/files/" + result.data.uuid, result.data);
      } else {
        toast.success(result.data.msg, reload);
      }
    }, function (status, data, headers) {
      if (window.failureFunc == undefined) {
        toast.failure("文件已上传到服务器，但是没有正确保存记录: " + getMessage(headers), undefined, -1);
      } else {
        window.failureFunc(status, data, headers)
      }
    });
  }
}

//======
function upload2ByThis(target, successFunc, failureFunc) {
  let file = target.files[0];

  let progress = target.getAttribute("data-progress");
  if (progress == undefined) {
    window.progressTemp = undefined;
  } else {
    window.progressTemp = $(progress);
  }

  getFileMd5(file, function (file, tmp_md5) {
    window.successFunc = successFunc;
    window.failureFunc = failureFunc;
    progressThis(1, 0);
    
    encryptions(tmp_md5, function (status, response) {
	  window.host = response.host;
	  window.serverId = response.serverId;
	  
	  let host = response.host;
      let userId = response.userId;
      let md5 = response.ciphertext;
      let url = host + '/encrypts/files/preload/' + userId;
      let body = { "md5": md5 };
      preuploadFile(url, body, function (response) {
        if (response.flag) {
          var result = {};
          result.code = 2;
          result.data = response.data;
          eventCallBack(result);
        } else {
          let formData = new FormData();
          formData.append("file", file);
          formData.append("ciphertext", md5);
          uploadFile(host + '/encrypts/files/upload/' + userId, formData);
        }
      });

    });

  }, function (file) {
    console.warn('oops, something went wrong.');
  }, function (file, p) {
    progressThis(1, p);
  });
}

function upload2File(files, tag, successFunc, failureFunc) {
  let file = files[0];
  window.progressTemp = $("#upload-progress");

  getFileMd5(file, function (file, tmp_md5) {
    window.successFunc = successFunc;
    window.failureFunc = failureFunc;
    progressThis(1, 0);
    encryptions(tmp_md5, function (status, response) {
	  window.host = response.host;
	  window.serverId = response.serverId;
	  
	  let host = response.host;
      let userId = response.userId;
      let md5 = response.ciphertext;
      let url = host + '/encrypts/files/preload/' + userId;
      let body = { "md5": md5 };
      preuploadFile(url, body, function (response) {
        if (response.flag) {
          var result = {};
          result.code = 2;
          result.data = response.data;
          eventCallBack(result, tag);
        } else {
          let formData = new FormData();
          formData.append("file", file);
          formData.append("ciphertext", md5);
          uploadFile(host + '/encrypts/files/upload/' + userId, formData, tag);
        }
      });

    });

  }, function (file) {
    console.warn('oops, something went wrong.');
  }, function (file, p) {
    progressThis(1, p);
  });

}

function preuploadFile(url, data, successFunc) {
  $.ajax({
    type: 'POST',
    url: url,
    data: JSON.stringify(data),
    contentType: "application/json",
    success: function (data, textStatus, jqXHR) {
      successFunc(data);
    },
    error: function (jqXHR) {
      console.error(jqXHR);
    }
  });
};

function progressThis(i, p) {
  if (window.progressTemp != undefined) {
    if (i == 1) {
      window.progressTemp.css("width", p + "%");
      if (p == 0) {
        window.progressTemp.text("0%");
      } else {
        window.progressTemp.text("正在准备" + p + "%");
      }
    } else if (i == 2) {
      window.progressTemp.css("width", p + "%");
      if (p == 0) {
        setTimeout(() => window.progressTemp.text("0%"), 1000);
        //window.progressTemp.text("0%");
      } else {
        window.progressTemp.text("正在上传" + p + "%");
      }
    }
  }
}

function encryptions(content, successFunc) {
  post('/files/encryptions', content, (status, data, headers) => {
    if (successFunc == undefined) {
      toast.success(data, reload);
    } else {
      successFunc(status, data);
    }
  });
}

function addFileMap(result, tag, successFunc, failureFunc) {
  let data = result.data;
  let content = {};
  content.serverId = window.serverId;
  content.uuid = result.uuid;
  if (tag) {
    content.tag = tag;
  }
  content.name = data.name;
  content.suffix = data.suffix;
  content.type = data.type;
  content.size = data.size;
  content.createTime = data.createTime;

  let json = JSON.stringify(content);

  post('/files/maps', json, (status, data, headers) => {
    if (successFunc == undefined) {
      toast.success(data, reload);
    } else {
      successFunc(status, data);
    }
  }, (status, data, headers) => {
	console.info(status);
	console.info(data);
	console.info(headers);
    if (failureFunc == undefined) {
      let obj = JSON.parse(data);
      toast.failure(obj.error, undefined, 3000);
    } else {
      failureFunc(status, data, headers);
    }
  });

}

function uploadFile(url, formData, tag) {
  $.ajax({
    type: "POST",
    url: url,
    data: formData,
    processData: false,
    contentType: false,
    xhr: function () {
      var xhr = $.ajaxSettings.xhr();
      if (xhr.upload) {
        //处理进度条的事件
        xhr.upload.addEventListener("progress", function (e) {
          var curr = e.loaded;
          var total = e.total;
          var process = Math.floor((curr / total) * 100);

          var result = {};
          result.code = 0;
          result.data = process
          eventCallBack(result, tag);
        }, false);
      }

      //加载完成的事件
      xhr.addEventListener("load", function (e) {
        var result = {};
        result.code = 1;
        eventCallBack(result, tag);
      }, false);

      //加载出错的事件
      xhr.addEventListener("error", function (e) {
        var result = {};
        result.code = -1;
        eventCallBack(result, tag);
      }, false);

      //加载取消的事件
      xhr.addEventListener("abort", function (e) {
        var result = {};
        result.code = -2;
        eventCallBack(result, tag);
      }, false);

      return xhr;
    },
    success: function (data) {
      var result = {};
      result.code = 2;
      result.data = data;
      eventCallBack(result, tag);
    },
    failure: function (data) {
      console.info(data);
      var result = {};
      result.code = -1;
      result.data = jqXHR.responseText;
      eventCallBack(result, tag);
    },
    error: function (jqXHR) {
      console.info(jqXHR);
      var result = {};
      result.code = -1;
      result.data = jqXHR.responseText;
      eventCallBack(result, tag);
    }
  });
};



